package com.floretexaminaction.rsaEncrypt;

import org.apache.commons.codec.binary.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class RSAEncrypt {
    private static Map<Integer, String> keyMap = new HashMap<>();
    private RSAEncrypt(){};

    public static void createRSA(){
        if(keyMap.isEmpty()){
            synchronized (RSAEncrypt.class){
                if (keyMap.isEmpty()){
                    createKeyPair();
                }
            }
        }
    }

    /**
     * 获取公钥
     * @return
     */
    public static String getPubKey(){
        return savePubKey("");
    }

    /**
     * 获取私钥
     * @return
     */
    public static String getPrvKey(){return savePriKey("");};

    /**
     * 随机生成密钥对
     * keyPairGenerator.initialize(1024) *数据不得超过128个字节
     */
    private static void createKeyPair(){
        try {
            // 1.生成公钥私钥对，基于RSA算法生成对象
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            // 2.初始化密钥生成器
            keyPairGenerator.initialize(1024);
            // 3.生成密钥对，保存在keyMap中
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 4.获取公钥
            PublicKey publicKey = keyPair.getPublic();
            // 4.获取私钥
            PrivateKey privateKey = keyPair.getPrivate();
            // 5.编码公钥得到字符串
            String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
            // 5.编码私钥得到字符串
            String privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded()));
            // 6.保存公钥和私钥
            //keyMap.put(0, publicKeyString); // 0表示公钥
            //keyMap.put(1, privateKeyString); // 1表示私钥
            savePubKey(publicKeyString);
            savePriKey(privateKeyString);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    private static String savePubKey(String pubKey){
        File file = new File("/Volumes/Document/Gitee/examinaction-system/Server/pubKey.txt");
        try {
            if (file.exists()){
                BufferedReader br = new BufferedReader(new FileReader(file));
                String str = br.readLine();
                return str;
            }else {
                file.createNewFile();
                BufferedWriter bw = new BufferedWriter(new FileWriter(file));
                if(pubKey.equals("")){createRSA();}
                bw.write(pubKey);
                bw.flush();
                bw.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    private static String savePriKey(String priKey){
        File file = new File("/Volumes/Document/Gitee/examinaction-system/Server/priKey.txt");
        try {
            if (file.exists()){
                BufferedReader br = new BufferedReader(new FileReader(file));
                String str = br.readLine();
                return str;
            }else {
                file.createNewFile();
                BufferedWriter bw = new BufferedWriter(new FileWriter(file));
                bw.write(priKey);
                bw.flush();
                bw.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }


    //public static void main(String[] args) {
    //    //生成公钥和私钥
    //    createRSA();
    //    //加密字符串
    //    String message = "1234";
    //    System.out.println("随机生成的公钥为:" + getPubKey());
    //    System.out.println("随机生成的私钥为:" +getPrvKey());
    //    String messageEn = encrypt(message,getPubKey());
    //    System.out.println(message + "\t加密后的字符串为:" + messageEn);
    //    String messageDe = decrypt(messageEn,getPrvKey());
    //    System.out.println("还原后的字符串为:" + messageDe);
    //}

    /**
     * RSA公钥加密
     * @param str 字符串
     * @param publicKey 公钥
     * @return 密文
     */
    public static String encrypt(String str, String publicKey){
        // base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = null;
        try {
            pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            //RSA加密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
            return outStr;
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return "";
    }

    /**
     * RSA私钥解密
     * @param str 公钥加密字符串
     * @param privateKey 私钥
     * @return 明文
     */
    public static String decrypt(String str, String privateKey){
        // 64位解码加密后的字符串
        //64位解码加密后的字符串
        byte[] inputByte = new byte[0];
        try {
            inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
            //base64编码的私钥
            byte[] decoded = Base64.decodeBase64(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            //RSA解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            String outStr = new String(cipher.doFinal(inputByte));
            return outStr;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return "";
    }
}
